﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Caliburn.Micro;
using Adalbertus.BudgetPlanner.Services;
using Adalbertus.BudgetPlanner.Domain;
using System.Diagnostics.Contracts;
using System.Windows.Input;
using System.ComponentModel;

namespace Adalbertus.BudgetPlanner.ViewModels.BudgetPlanning
{
    public class ExpensesViewModel : Screen
    {
        public BudgetVM Budget { get; set; }

        private ICashFlowService _cashFlowService;
        private IBudgetService _budgetService;
        public ExpensesViewModel(ICashFlowService cashFlowService, IBudgetService budgetService)
        {
            Contract.Requires<ArgumentNullException>(cashFlowService != null, "cashFlowService");
            Contract.Requires<ArgumentNullException>(budgetService != null, "budgetService");

            _cashFlowService = cashFlowService;
            _budgetService   = budgetService;

            _cashFlows = new BindableCollectionExt<CashFlowVM>();

            _expenseValues = new BindableCollectionExt<decimal>();
            _expenseValues.CollectionChanged += (s, e) =>
            {
                NotifyOfPropertyChange(() => ExpenseTotalValue);
                NotifyOfPropertyChange(() => CanAddExpense);
            };
            ExpenseDate = DateTime.Now;

            _budgetExpenses = new BindableCollectionExt<ExpenseVM>();
        }

        private BindableCollectionExt<CashFlowVM> _cashFlows;
        public BindableCollectionExt<CashFlowVM> CashFlows
        {
            get
            {
                Contract.Ensures(Contract.Result<BindableCollectionExt<CashFlowVM>>() != null);
                return _cashFlows;
            }
        }

        private CashFlowVM _expenseCashFlow;
        public CashFlowVM ExpenseCashFlow
        {
            get { return _expenseCashFlow; }
            set
            {
                _expenseCashFlow = value;
                NotifyOfPropertyChange(() => ExpenseCashFlow);
                NotifyOfPropertyChange(() => CanAddExpense);
            }
        }

        private DateTime _expenseDate;
        public DateTime ExpenseDate
        {
            get { return _expenseDate; }
            set
            {
                _expenseDate = value;
                NotifyOfPropertyChange(() => ExpenseDate);
                NotifyOfPropertyChange(() => CanAddExpense);
            }
        }

        private decimal _expenseValue;
        public decimal ExpenseValue
        {
            get
            {
                return _expenseValue;
            }
            set
            {
                _expenseValue = value;
                NotifyOfPropertyChange(() => ExpenseValue);
                NotifyOfPropertyChange(() => CanAddExpense);
            }
        }

        public decimal ExpenseTotalValue
        {
            get
            {
                return ExpenseValues.Sum();
            }
        }

        private BindableCollectionExt<decimal> _expenseValues;
        public BindableCollectionExt<decimal> ExpenseValues
        {
            get
            {
                Contract.Ensures(Contract.Result<BindableCollectionExt<decimal>>() != null);
                return _expenseValues;
            }
        }


        private string _expenseDescription;
        public string ExpenseDescription
        {
            get { return _expenseDescription; }
            set
            {
                _expenseDescription = value;
                NotifyOfPropertyChange(() => ExpenseDescription);
            }
        }

        #region Focus properties
        private bool _isExpenseDescriptionFocused;

        public bool IsExpenseDescriptionFocused
        {
            get { return _isExpenseDescriptionFocused; }
            set
            {
                _isExpenseDescriptionFocused = value;
                NotifyOfPropertyChange(() => IsExpenseDescriptionFocused);
            }
        }

        #endregion

        public bool CanAddExpense
        {
            get
            {
                if (ExpenseValues.Any())
                {
                    return (ExpenseCashFlow != null) && (ExpenseTotalValue > 0);
                }
                return (ExpenseCashFlow != null) && (ExpenseValue > 0);
            }
        }

        public void AddExpense()
        {
            decimal expenseValue = ExpenseValue;
            if (ExpenseValues.Any())
            {
                expenseValue = ExpenseTotalValue;
            }
            var expense = Budget.WrappedBudget.AddExpense(ExpenseCashFlow.WrappedCashFlow, expenseValue, ExpenseDescription, ExpenseDate);
            _budgetService.Save(Budget.WrappedBudget);
            ExpenseVM expenseVM = new ExpenseVM(expense);
            BudgetExpenses.Add(expenseVM);

            ExpenseValues.Clear();
            ExpenseValue = 0;
            ExpenseDescription = string.Empty;
        }

        public void AddExpenseValueToCalculator()
        {
            ExpenseValues.Add(ExpenseValue);
            ExpenseValue = 0;
        }

        public void RemoveExpenseValueFromCalculator(decimal value)
        {
            // TODO: I guess it is a bug in caliburn - caliburn thinks that this method is invoked from BudgetPlanView
            ExpenseValues.Remove(value);
        }

        public void MoveToExpenseDescription()
        {
            IsExpenseDescriptionFocused = false;
            IsExpenseDescriptionFocused = true;
        }

        #region Expenses grid
        private BindableCollectionExt<ExpenseVM> _budgetExpenses;
        public BindableCollectionExt<ExpenseVM> BudgetExpenses { get { return _budgetExpenses; } }

        private void BudgetExpensesPropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            switch(e.PropertyName)
            {
                
            }
        }
        #endregion


        protected override void OnActivate()
        {
            base.OnActivate();

            IEnumerable<CashFlow> cashFlows = _cashFlowService.GetAll();

            CashFlows.Clear();
            CashFlows.AddRange(cashFlows.Select(x => new CashFlowVM(x)));

            BudgetExpenses.Clear();
            BudgetExpenses.AddRange(Budget.WrappedBudget.Expenses.Select(x => new ExpenseVM(x)));
            BudgetExpenses.PropertyChanged += BudgetExpensesPropertyChanged;
        }
    }
}
